package asteroids.model;

import asteroids.CollisionListener;
import be.kuleuven.cs.som.annotate.*;

/**
 * @version 0.3
 * @author  Tandan Sunil, Van Deun Luca
 */
public class Bullet extends Entity {
	
	/**
	 * Initialize this new Bullet with given coordinates and speed according to x- and y- axes, radius and direction.
	 *  
	 * @param source
	 * 		  The ship that initialized this bullet.
	 * @pre    ...
	 * 		 | Entity.isValidEntity(source)
	 * @post   ...
	 * 		 | new.getXCoordinate() == xCoordinate + source.getRadius()*Math.cos(source.getAngle()
	 * @post   ...
	 * 		 | new.getYCoordinate() == yCoordinate + source.getRadius()*Math.sin(source.getAngle()
	 * @post   ...
	 * 		 | new.getRadius() == bulletRadius
	 * @post   ...
	 * 		 | new.getXVelocity == bulletSpeed*Math.cos(source.getAngle())
	 *		 | new.getYVelocity == bulletSpeed*Math.sin(source.getAngle())
	 * @post   ...
	 * 		 | new.getMass() == 4/3*Math.PI*Math.pow(radius, 3)*bulletDensity
	 * @throws IllegalArgumentException
	 * 		   The given radius is invalid.
	 * 		 | ! isValidRadius(radius)
	 * @throws IllegalArgumentException
	 * 		   The given coordinate is invalid.
	 * 		 | ! isValidCoordinate(xCoordinate) || ! isValidCoordinate(yCoordinate)
	 */
	public Bullet(Ship source){
		super(source.getXCoordinate()+source.getRadius()*Math.cos(source.getAngle())+bulletRadius*Math.cos(source.getAngle()), source.getYCoordinate()+ source.getRadius()*Math.sin(source.getAngle())+bulletRadius*Math.sin(source.getAngle()),
				bulletSpeed*Math.cos(source.getAngle()), bulletSpeed*Math.sin(source.getAngle()), bulletRadius, 4/3*Math.PI*Math.pow(bulletRadius, 3)*bulletDensity,
				bulletSpeed);
		assert(Entity.isValidEntity(source));
		this.source = source;
		(source.getWorld()).addAsEntity(this);
	}

	public static final double bulletRadius = 3; // in km
	public static final double bulletDensity = 7.8* Math.pow(10,12); //in kg/km^3  
	public static final double bulletSpeed = 250; //in km/s
	
	private final Ship source;
	
	/**
	 *  Returns the source of this bullet
	 */
	@Basic
	public Ship getSource(){
		return source;
	}
	
	
	
	////////////////////////////////////////////////////////////////////////////
	//								Collision   							  //
	////////////////////////////////////////////////////////////////////////////
	
	
	
	private int collisionNb=0;
	private int allowedCollision=1;
	
	/**
	 * Increases of the number of times this bullet has collided with the boundaries of the world by 1.
	 *
	 * @post	...
	 *			|new.getCollisionNb() = getCollisionNb() + 1;
	 * @post    ...
	 * 			| if((getCollisionNb() + 1)> getNbAllowedCollision())
	 * 			|	then this.die()
	 */
	public void countCollision(){
		collisionNb++;
		if(collisionNb > getNbAllowedCollsion()){
			die();
		}
	}
	
	/**
	 * Returns the number of times this bullet has collided with the boundaries of world.
	 */
	@Basic
	public int getCollisionNb(){
		return collisionNb;
	}
	
	/**
	 * Sets the number of allowed collision with the boundaries of the world before the bullet dies.
	 * @param nb
	 * 			| The number to be set as the new value for allowed collisions.
	 * @pre   The given integer must lie between 0 and Integer.MAX_VALUE.
	 * 			| nb>=0 && nb<= Integer.MAX_VALUE
	 * @post	...
	 * 			| new.getNbAllowedCollision() == nb;
	 */
	public void setNbAllowedCollision(int nb){
	assert(nb>=0 && nb<= Integer.MAX_VALUE);
		collisionNb = nb;
	}
	
	/**
	 * Returns the allowed collisions number with the boundaries of the world before the bullet dies.
	 */
	@Basic
	public int getNbAllowedCollsion(){
	return allowedCollision;	
	}
	
	/**
	 * Checks whether the given entity is related to this bullet.
	 * @param entity
	 * 		  The entity to be checked.
	 * @pre   ...
	 * 		| Entity.isValidEntity(entity)
	 * @return ...
	 * 		| if(entity instanceof Ship)
	 * 		|  then result == (getSource() == entity)
	 * 		| if(entity instanceof Bullet)
	 * 		|  then result == (getSource() == ((Bullet)entity).getSource())
	 * 		| else
	 * 		|  result == false;
	 * 		  
	 */
	public boolean isRelated(Entity entity){
		assert(Entity.isValidEntity(entity));
		if(entity instanceof Ship){
			return getSource()==entity;
		}
		if(entity instanceof Bullet){
			return getSource() == ((Bullet)entity).getSource();
		}
		return false;
	}

	/**
	 * Resolves the boundary collision of the given this.
	 * @param collisionListener
	 * 			The collision listener that has to be called. 
	 * @post	...
	 * 			|countCollision()
	 * 
	 * @post    If the this collides with right or left boundaries of its world then its velocity according to the x-axis is reversed. 
	 * 			|if(!isTerminated() && ((Util.fuzzyEquals(this.getXCoordinate() + this.getRadius(),this.getWorld().getWidth()))	
	 *		    |    || (Util.fuzzyEquals(this.getXCoordinate() - this.getRadius(), 0))))
	 *		    | then 
	 *		    |    (new this).getXVelocity()== -1 * this.getXVelocity() 
	 *			|     && (new this).getYVelocity() == this.getYVelocity()
	 * @post 	If the this collides with upper or lower boundaries of its world then its velocity according to the y-axis is reversed.
	 * 			| if(!isTerminated() && ((Util.fuzzyEquals(this.getYCoordinate() + this.getRadius(),this.getWorld().getHeight()))	
	 *		    |    || (Util.fuzzyEquals(this.getYCoordinate() - this.getRadius(), 0))))
	 *		    | then 
	 *		    |    (new this).getYVelocity()== -1 * this.getYVelocity() 
	 *			|     && (new this).getXVelocity() == this.getXVelocity()
	 *
	 */
	@Override
	public void collideWithBoundaries(CollisionListener collisionListener){
		countCollision();
		if (this.isTerminated())
		return;
		super.collideWithBoundaries(collisionListener);
		}
	
	/**
	 * Checks whether this bullet can bounce with the given entity.	 
	 * @param	entity1
	 * 			The entity with which it has to be checked.
	 * @pre  	The given entity is valid.
	 * 			| Entity.isValidEntity(entity1)
	 * @return  If the given entity is instance of ship then it is true else entity1.canBounceWith(this).
	 * 			| if(entity1 instanceof Asteroid || (entity1 instanceof Ship) || (entity1 instanceof Bullet))
	 * 			|  then result== false
	 * 			| else result == entity1.canBounceWith(this)
	 */
	@Override
	public boolean canBounceWith(Entity entity1) {
		assert(Entity.isValidEntity(entity1));
		if(entity1 instanceof Asteroid || (entity1 instanceof Ship) || (entity1 instanceof Bullet))
			return false;
		else return entity1.canBounceWith(this);
		
	}
	
	/**
	 * Resolves a collision between this bullet and another entity.
	 * @param other
	 * 		  | The entity that collides with this bullet.
	 * @param collisionListener
	 * 		  | The collision listener to be called.
	 * @pre   The given entity is valid.
	 * 		  | Entity.isValidEntity(other)
	 * 
	 * @post  ...
	 * 		 | if( other instanceof Bullet)
	 * 		 | 	then if(this.getSource() == other.getSource())
	 * 		 |   then return
	 * 		 | else if (other instanceof Ship)
	 * 		 | 	then if (this.getSource() == other) 
	 * 		 |	 then return
	 * 		 | this.die() and other.die() 
	 * 
	 */
	@Override
	public void collide(Entity other,CollisionListener collisionListener) {
		assert (Entity.isValidEntity(other));
		if (other instanceof Bullet) {
			  if (((Bullet) other).getSource() == this.getSource())
					return;
		}
			
		else if (other instanceof Ship){
			if(this.getSource() == other)
				return;
		}
		else if(collisionListener!=null){
			double[] collisionPosition = getCollisionPosition(other);
			collisionListener.objectCollision(other, this, collisionPosition[0], collisionPosition[1]);}
		this.die();
		other.die();
	}

	


}
